<?php
/**************************************************************************
 * Naanal PHP Framework, Simple, Efficient and Developer Friendly
 * Ver 3.0, Copyright (C) <2010>  <Tamil Amuthan. R>
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ************************************************************************/
include_once (dirname(__FILE__)."/ClsLArray.php");
class ClsLTokenizeBase extends ClsNaanalBusiness
{
    public $statementType=-1;
    public $statementName="";
    public $arrayParam=array();
    public $data="";
    public $startToken=0;
    protected $arrToken=array();
    ///all tokens except white spaces
    protected $arrValidTokenPos=array();
    ///all tokens except white spaces and a valid element such as $this->name (or) action() etc.
    protected $arrValidElementPos=array();
    protected $arrValidStatementPos=array();
    private $arrElementSeparator=array(";",":","=","==","===","!","!=","!==","<",">","<=","=>",">=","@","++","--","+","-","~","+=","-=");
    private $arrValidFirstItem=array('+','-','!','~','++','--','@','[','(','\$');
    private $arrValidFirstItemTokenNumber=array();
    private $currentElementPointer=0;
    protected $arrOpenToken=array();
    protected $arrCloseToken=array();
    public $arrStatementObject=array();
    
    private $isSingleStatment=false;

    public function __construct(&$arrToken,$isSingleStatment=false)
    {
        /**
         * in php @ token, at the beginning of statement, is used to specify in function call. so ignoring it
         */
        if(!is_array($arrToken[0]) && $arrToken[0]=="@")
        {
            $this->startToken=1;
        }
        $this->statementType=$arrToken[$this->startToken][0];
        $this->statementName=$arrToken[$this->startToken][1];
        
        if(empty($arrToken))
        {
            $this->arrError[]=array("error"=>"Empty Tokens","vars"=>  $vars,"this"=>$this);
            trace($this->arrError);
        }
        $this->arrValidFirstItemTokenNumber=array(T_OPEN_TAG,T_TRY,T_CATCH,T_ELSE,T_ELSEIF,T_IF,T_WHILE,T_SWITCH,T_DO,T_EXIT,T_VARIABLE,T_FUNCTION,T_FUNCTION,T_ISSET,T_EMPTY,T_ARRAY,T_INCLUDE,T_INCLUDE_ONCE,T_EVAL,T_REQUIRE,T_REQUIRE_ONCE,T_PRINT,T_NEW,T_STATIC);
        
        $this->isSingleStatment=$isSingleStatment;
        $firstToken=$arrToken[0];

        /**
         * Strictly validating first token of statement
         */
        if(is_array($firstToken))
        {
            if(!in_array($firstToken[0], $this->arrValidFirstItemTokenNumber))
            {
                traceDirect("Unexpected First Token in a statement : ".  token_name($firstToken[0]));
            }
        }
        else
        {
            if(!in_array($firstToken, $this->arrValidFirstItem))
            {
                traceDirect("Unexpected First Token in a statement : ".$firstToken);
            }
        }

        /**
         * If the statement is not a block level element, 
         * pushing tokens into $this->arrOpenToken and $this->arrCloseToken and keep remaining token in the $this->arrToken
         * Then process tokens for find valid element and statement
         */
        if(!$this->isSingleStatment)
        {
            if($firstToken[0]===T_FUNCTION || $firstToken[0]===T_IF || $firstToken[0]===T_FOR || $firstToken[0]===T_WHILE || $firstToken[0]===T_TRY || $firstToken[0]===T_CATCH || $firstToken[0]===T_ELSE || $firstToken[0]===T_ELSEIF)
            {
                $token=array_shift($arrToken);
                $this->arrOpenToken[]=$token;
                while($token=array_shift($arrToken))
                {
                    $this->arrOpenToken[]=$token;
                    if(!is_array($token) && $token=="{") 
                    {
                        ///move the pointer to start of valid token
                        while($arrToken[0][0]===T_WHITESPACE || $arrToken[0][0]===T_COMMENT)
                        {
                            $token=array_shift($arrToken);
                            $this->arrOpenToken[]=$token;
                        }
                        break;
                    }
                }
                while($token=array_pop($arrToken))
                {
                    array_unshift($this->arrCloseToken, $token);//trace($token);
                    if(!is_array($token) && $token=="}") 
                    {
                        break;
                    }
                }
            }
            else if($firstToken[0]===T_OPEN_TAG)
            {
                $token=array_shift($arrToken);
                $this->arrOpenToken[]=$token;
                while($token=array_shift($arrToken))
                {
                    if(is_array($token))
                    {
                        if($token[0]!==T_COMMENT && $token[0]!==T_WHITESPACE)
                        {
                            array_unshift($arrToken,$token);
                            break;
                        }
                    }
                    $this->arrOpenToken[]=$token;
                }
                while($token=array_pop($arrToken))
                {
                    if(is_array($token)) 
                    {
                        if($token[0]===T_CLOSE_TAG)
                        {
                            array_push($this->arrCloseToken, $token);
                            break;
                        }
                        else
                        {
                            array_push($arrToken,$token);
                            break;
                        }
                    }
                    else
                    {
                        ///if there is no end php tag and if the last token is not an array ie. open brace "{" or open bracker "(" etc.
                        array_push($arrToken,$token);
                        break;
                    }
                    array_push($this->arrCloseToken, $token);
                }
            }
        }
        $this->arrToken=$arrToken;
        $this->processToken();
        if(!$this->isSingleStatment) 
        {
            $this->processStatementObjects();
        }
    }
    
    public function getAssignmentDataAsArrayObject()
    {
        if($this->statementType===T_VARIABLE)
        {
            trace($this->arrValidElementPos);
        }
    }
    
    /**
     * returns -1 if not array. else return the index
     * @return int
     */
    public function isArrayAssignment()
    {
        if($this->statementType===T_VARIABLE)
        {
            $nextTokenIndex=0;
            while(($nextTokenIndex=$this->getNextToken($nextTokenIndex))>-1)
            {
                if(is_array($this->arrToken[$nextTokenIndex]))
                {
                    //$this->arrayParam[]=$arrToken[$nextTokenIndex][1];
                }
                else
                {
                    if($this->arrToken[$nextTokenIndex]=="=")
                    {
                        $data="";
                        for($m=$nextTokenIndex+1;$m<count($this->arrToken);$m++)
                        {
                            if(is_array($this->arrToken[$m]))
                            {
                                if($this->arrToken[$m][0]===T_ARRAY)
                                {
                                    return $m;
                                }
                            }
                            else
                            {
                                if($this->arrToken[$m]==";")
                                {
                                    break;
                                }
                            }
                        }
                        break;
                    }
                }
            }
        }
        return -1;
    }
    
    public function isSingleStatement()
    {
        return $this->isSingleStatment;
    }
    public function save()
    {
        return (String)$this;
    }
    public function __toString() 
    {
        $content="";
        /*if(empty($this->arrOpenToken))
        {*/
            foreach($this->arrToken as $token)
            {
                if(is_array($token))
                {
                    $content=$content.$token[1];
                }
                else
                {
                    $content=$content.$token;
                }
            }
        /*}
        else
        {
            if(!empty($this->arrOpenToken))
            {
                foreach($this->arrOpenToken as $token)
                {
                    if(is_array($token))
                    {
                        $content=$content.$token[1];
                    }
                    else
                    {
                        $content=$content.$token;
                    }
                }
            }
            foreach($this->arrStatementObject as $objStatement)
            {
                $content=$content.$objStatement;
            }
            if(!empty($this->arrCloseToken))
            {
                foreach($this->arrCloseToken as $token)
                {
                    if(is_array($token))
                    {
                        $content=$content.$token[1];
                    }
                    else
                    {
                        $content=$content.$token;
                    }
                }
            }
        }*/
        return $content;
    }
    /**
     * get Type Of Element
     * @param type $validTokenPos
     * @return type
     */
    protected function getElementType($validTokenPos)
    {
        $prevPos=-1;
        $prevToken=null;
        if(isset($this->arrValidTokenPos[$validTokenPos-1]))
        {
            $prevPos=$this->arrValidTokenPos[$validTokenPos-1];
            $prevToken=$this->arrToken[$prevPos];
        }
        $nextPos=-1;
        $nextToken=null;
        if(isset($this->arrValidTokenPos[$validTokenPos+1]))
        {
            $nextPos=$this->arrValidTokenPos[$validTokenPos+1];
            $nextToken=$this->arrToken[$nextPos];
        }
        $secondNextPos=-1;
        $secondNextToken=null;
        if(isset($this->arrValidTokenPos[$validTokenPos+2]))
        {
            $secondNextPos=$this->arrValidTokenPos[$validTokenPos+2];
            $secondNextToken=$this->arrToken[$secondNextPos];
        }
        $thirdNextPos=-1;
        $thirdNextToken=null;
        if(isset($this->arrValidTokenPos[$validTokenPos+3]))
        {
            $thirdNextPos=$this->arrValidTokenPos[$validTokenPos+3];
            $thirdNextToken=$this->arrToken[$thirdNextPos];
        }
        $startPos=$this->arrValidTokenPos[$validTokenPos];
        $startToken=$this->arrToken[$startPos];
        
        if(!is_array($startToken))
        {
            
            if(in_array($startToken, $this->arrElementSeparator))
            {
                return array("type"=>"elementSeparator","endPos"=>$validTokenPos, "element"=>$startToken, "prevValidToken"=>$prevToken, "nextValidToken"=>$nextToken, "nextSecondValidToken"=>$secondNextToken, "nextThirdValidToken"=>$thirdNextToken);
            }
            else if($startToken=="{")
            {
                return array("type"=>"braceStart","endPos"=>$validTokenPos, "element"=>$startToken, "prevValidToken"=>$prevToken, "nextValidToken"=>$nextToken, "nextSecondValidToken"=>$secondNextToken, "nextThirdValidToken"=>$thirdNextToken);
            }
            else if($startToken=="}")
            {
                return array("type"=>"braceEnd","endPos"=>$validTokenPos, "element"=>$startToken, "prevValidToken"=>$prevToken, "nextValidToken"=>$nextToken, "nextSecondValidToken"=>$secondNextToken, "nextThirdValidToken"=>$thirdNextToken);
            }
            else //if($this->arrToken[$startPos]=="(" || $this->arrToken[$startPos]==")" || $this->arrToken[$startPos]=="[" || $this->arrToken[$startPos]=="]")
            {
                traceDirect("Unexpected Token : {$this->arrToken[$startPos]} in content : {$this} at position : {$startPos}");
            }
        }
        else
        {
            if($startToken[0]===T_ARRAY) 
            {
                if($nextToken=="(")
                {
                    $endPos=$this->getBlockEndByValidPos($validTokenPos+1, "(",")");
                    return array("type"=>"array","endPos"=>$endPos, "element"=>$startToken, "prevValidToken"=>$prevToken, "nextValidToken"=>$nextToken, "nextSecondValidToken"=>$secondNextToken, "nextThirdValidToken"=>$thirdNextToken);
                }
                else
                {
                    traceDirect("Unexpected Token");
                }
            }
            else if($this->arrToken[$startPos][0]===T_VARIABLE)
            {
                if(is_array($nextToken))
                {
                    if($nextToken[0]==T_OBJECT_OPERATOR)
                    {
                        if(!is_array($this->arrToken[$secondNextPos])) traceDirect("Unexpected Token");
                        if(is_array($thirdNextToken))
                        {
                            traceDirect("Unexpected Token");
                        }
                        else
                        {
                            if($thirdNextToken=="(")
                            {
                                $endPos=$this->getBlockEndByValidPos($validTokenPos+3, "(", ")");
                                return array("type"=>"object_functioncall","endPos"=>$endPos, "element"=>$startToken, "prevValidToken"=>$prevToken, "nextValidToken"=>$nextToken, "nextSecondValidToken"=>$secondNextToken, "nextThirdValidToken"=>$thirdNextToken);
                            }
                            else if($thirdNextToken=="[")
                            {
                                $endPos=$this->getBlockEndByValidPos($validTokenPos+3, "[", "]");
                                return array("type"=>"object_array","endPos"=>$endPos, "element"=>$startToken, "prevValidToken"=>$prevToken, "nextValidToken"=>$nextToken, "nextSecondValidToken"=>$secondNextToken, "nextThirdValidToken"=>$thirdNextToken);
                            }
                            else if(in_array($thirdNextToken, $this->arrElementSeparator))
                            {
                                return array("type"=>"object_var","endPos"=>$validTokenPos+2, "element"=>$startToken, "prevValidToken"=>$prevToken, "nextValidToken"=>$nextToken, "nextSecondValidToken"=>$secondNextToken, "nextThirdValidToken"=>$thirdNextToken);
                            }
                            else
                            {
                                traceDirect("Unexpected Token");
                            }
                        }
                    }
                    else
                    {
                        traceDirect("Unexpected Token :".  token_name($nextToken[0]));
                    }
                }
                else if($this->arrToken[$nextPos]=="=")
                {
                    return array("type"=>"assignmentVariable","endPos"=>$validTokenPos, "element"=>$startToken, "prevValidToken"=>$prevToken, "nextValidToken"=>$nextToken, "nextSecondValidToken"=>$secondNextToken, "nextThirdValidToken"=>$thirdNextToken);
                    trace(token_name($this->arrToken[$startPos][0]));
                }
                else if($this->arrToken[$nextPos]=="(")
                {
                    return array("type"=>"function","subType"=>"functionCall","endPos"=>$validTokenPos, "element"=>$startToken, "prevValidToken"=>$prevToken, "nextValidToken"=>$nextToken, "nextSecondValidToken"=>$secondNextToken, "nextThirdValidToken"=>$thirdNextToken);
                }
                else if($this->arrToken[$nextPos]=="[")
                {
                    return array("type"=>"variable","subType"=>"array","endPos"=>$validTokenPos, "element"=>$startToken, "prevValidToken"=>$prevToken, "nextValidToken"=>$nextToken, "nextSecondValidToken"=>$secondNextToken, "nextThirdValidToken"=>$thirdNextToken);
                }
                else if(in_array($this->arrToken[$nextPos], $this->arrElementSeparator))
                {
                    return array("type"=>"variable","subType"=>"none","endPos"=>$validTokenPos, "element"=>$startToken, "prevValidToken"=>$prevToken, "nextValidToken"=>$nextToken, "nextSecondValidToken"=>$secondNextToken, "nextThirdValidToken"=>$thirdNextToken);
                }
                else
                {
                    traceDirect("Unexpected Token");
                }
            }
            else
            {
                if(is_array($this->arrToken[$nextPos])) traceDirect("UnExpected Token");
                if($this->arrToken[$nextPos]=="(")
                {
                    $endPos=$this->getBlockEndByValidPos($validTokenPos+1, "(", ")");
                    return array("type"=>"functioncall","endPos"=>$endPos, "element"=>$startToken, "prevValidToken"=>$prevToken, "nextValidToken"=>$nextToken, "nextSecondValidToken"=>$secondNextToken, "nextThirdValidToken"=>$thirdNextToken);
                }
                else if($this->arrToken[$nextPos]=="[")
                {
                    $endPos=$this->getBlockEndByValidPos($validTokenPos+1, "[", "]");
                    return array("type"=>"array","endPos"=>$endPos, "element"=>$startToken, "prevValidToken"=>$prevToken, "nextValidToken"=>$nextToken, "nextSecondValidToken"=>$secondNextToken, "nextThirdValidToken"=>$thirdNextToken);
                }
                else
                {
                    traceDirect("Unexpected Token");
                }
            }
        }
    }
    protected function processToken()
    {
        $arrToken=$this->arrToken;
        $tokenLencount=count($this->arrToken);
        for($i=0;$i<$tokenLencount;$i++)
        {
            $token=$this->arrToken[$i];
            if(is_array($token) && ($token[0]==T_WHITESPACE || $token[0]==T_COMMENT || $token[0]==T_DOC_COMMENT)) continue;
            $this->arrValidTokenPos[]=$i;
        }
        $arrValidTokenPos=$this->arrValidTokenPos;
        //$this->arrValidElementPos[]=$this->arrValidTokenPos[0];
        $previousToken=0;$arrArrType=array();
        $this->arrValidElementPos=array();
        for($j=1;$j<count($this->arrValidTokenPos);$j++)
        {
            $arrType=$this->getElementType($j);$arrArrType[]=$arrType;
            //if($arrType["type"]=="elementSeparator")
            //{
               $this->arrValidElementPos[]=$previousToken;
            //}
            $previousToken=$j;
            $j=$arrType["endPos"];
            /*trace($arrType);
            $firstToken=$this->arrToken[$this->arrValidTokenPos[$j]];
            if(is_array($firstToken))
            {
                $token=$this->arrToken[$this->arrValidTokenPos[$j+1]];
                if(is_array($token) && ($token[0]===T_OBJECT_OPERATOR))
                {
                    $arrType=$this->getElementType($j+2);
                    $this->arrValidElementPos[]=$previousToken;
                    $previousToken=$j;
                    $j=$arrType["endPos"];
                    continue;
                }
                else if(in_array($token, $this->arrElementSeparator))
                {
                    $this->arrValidElementPos[]=$previousToken;
                    $previousToken=$j;
                }
                else
                {
                    $this->arrValidElementPos[]=$previousToken;
                    $previousToken=$j;
                }
            }
            else
            {
                if($firstToken=="[")
                {
                    $this->arrValidElementPos[]=$previousToken;
                    $previousToken=$j;
                    $blockEndValidPos=$this->getBlockEndByValidPos($j, "[", "]");
                    $j=$blockEndValidPos;
                    continue;
                }
                else if($firstToken=="(")
                {
                    $this->arrValidElementPos[]=$previousToken;
                    $previousToken=$j;
                    $blockEndValidPos=$this->getBlockEndByValidPos($j, "(", ")");
                    $j=$blockEndValidPos;
                    continue;
                }
                else if($firstToken=="{")
                {
                    $this->arrValidElementPos[]=$previousToken;
                    $previousToken=$j;
                    $blockEndValidPos=$this->getBlockEndByValidPos($j, "{", "}");
                    $j=$blockEndValidPos;
                    continue;
                }
                else
                {
                    $this->arrValidElementPos[]=$previousToken;
                    $previousToken=$j;
                }
            }*/
        }
        ///the previousToken points last valid element store it
        if($previousToken!==0) $this->arrValidElementPos[]=$previousToken;

        $previousElement=0;
        for($k=1;$k<count($this->arrValidElementPos);$k++)
        {
            $validTokenPreviousPos=$this->arrValidElementPos[$previousElement];
            $tokenPreviousPos=$this->arrValidTokenPos[$validTokenPreviousPos];
            $tokenPrevious=$this->arrToken[$tokenPreviousPos];
            if(is_array($tokenPrevious) && ($tokenPrevious[0]===368 || $tokenPrevious[0]===369))
            {
                $this->arrValidStatementPos[]=$previousElement;
                $previousElement=$k;
                continue;
            }
            
            $validTokenPos=$this->arrValidElementPos[$k];
            $tokenPos=$this->arrValidTokenPos[$validTokenPos];
            $token=$this->arrToken[$tokenPos];
            if(is_array($token))
            {
                continue;
            }
            
            if($token==";")
            {
                $this->arrValidStatementPos[]=$previousElement;
                $k=$k+1;
                $previousElement=$k;
            }
            else if($token=="{")
            {
                $this->arrValidStatementPos[]=$previousElement;
                $k=$k+1;
                $previousElement=$k;
            }
        }
    }
    public function getValidTokens()
    {
        $tmparr=array();
        for($ttt=0;$ttt<count($this->arrValidElementPos);$ttt++)
        {
            $tmppos=$this->arrValidTokenPos[$this->arrValidElementPos[$ttt]];
            $tmpendpos=isset($this->arrValidElementPos[$ttt+1])?$this->arrValidTokenPos[$this->arrValidElementPos[$ttt+1]]:count($this->arrToken);
            $tmp="";
            for($sss=$tmppos;$sss<$tmpendpos;$sss++)
            {
                $tmp=$tmp.(is_array($this->arrToken[$sss])?$this->arrToken[$sss][1]:$this->arrToken[$sss]);
            }
            $tmparr[]=$tmp;
        }
        return $tmparr;
    }
    /**
     * process total tokens and store all the valid statements in array
     * @return array of statement(list of tokens related to statement)
     */
    private function getValidStatementArray()
    {
        $tmparr=array();
        for($ttt=0;$ttt<count($this->arrValidStatementPos);$ttt++)
        {
            $posValidElement=$this->arrValidStatementPos[$ttt];
            $posValidToken=$this->arrValidElementPos[$posValidElement];
            $posToken=$this->arrValidTokenPos[$posValidToken];
            $token=$this->arrToken[$posToken];
            //trace($token);
            if(isset($this->arrValidStatementPos[$ttt+1]))
            {
                $posValidElement2=$this->arrValidStatementPos[$ttt+1];
                $posValidToken2=$this->arrValidElementPos[$posValidElement2];
                $posToken2=$this->arrValidTokenPos[$posValidToken2];
                ///move pointer to previous statement's last token
                $posToken2=$posToken2-1;
                //$token2=$this->arrToken[$posToken2];
            }
            else
            {
                $posToken2=count($this->arrToken)-1;
                //$token2=$this->arrToken[$posToken2];
            }

            $tmp=array();
            for($sss=$posToken;$sss<=$posToken2;$sss++)
            {
                $tmp[]=$this->arrToken[$sss];
            }
            $tmparr[]=$tmp;
        }
        return $tmparr;
    }
    private function processStatementObjects()
    {
        $arr=$this->getValidStatementArray();
        for($i=0;$i<count($arr);$i++)
        {
            if($arr[$i][0][0]===T_FUNCTION || $arr[$i][0][0]===T_IF || $arr[$i][0][0]===T_FOR || $arr[$i][0][0]===T_WHILE || $arr[$i][0][0]===T_TRY || $arr[$i][0][0]===T_CATCH || $arr[$i][0][0]===T_ELSE || $arr[$i][0][0]===T_ELSEIF)
            {
                //$tmpObj=new ClsBTokenize();
                //$tmpObj->loadToken($arr[$i]);
                $this->arrStatementObject[]=new ClsLStatement($arr[$i]);//$tmpObj;
                $this->arrToken=array();
            }
            else
                $this->arrStatementObject[]=new ClsLStatement($arr[$i],true);
        }
        //return $tmparr;
    }
    private function getValidStatements()
    {
        $tmparr=array();
        $arr=$this->getValidStatementArray();
        for($i=0;$i<count($arr);$i++)
        {
            $tmp="";
            for($j=0;$j<count($arr[$i]);$j++)
            {
                $tmp=$tmp.(is_array($arr[$i][$j])?$arr[$i][$j][1]:$arr[$i][$j]);
            }
            $tmparr[]=$tmp;
        }
        return $tmparr;
    }
    private function getValidTokenContent()
    {
        $tmparr=$this->getValidTokens();
        $contentStatement=implode("",$tmparr);
        return $contentStatement;
    }
    private function getValidStatementContent()
    {
        $tmparr=$this->getValidStatements();
        $contentStatement=implode("",$tmparr);
        return $contentStatement;
    }
    protected function getNextToken($tokenPos) 
    {
        $tokenLencount=count($this->arrToken);
        for($i=$tokenPos+1;$i<$tokenLencount;$i++)
        {
            $token=$this->arrToken[$i];
            if(is_array($token) && $token[0]==T_WHITESPACE) continue;
            return $i;
        }
        return -1;
    }
    protected function getBlockEndByValidPos($blockPosStart,$blockStartDelimiter="{",$blockEndDelimiter="}")
    {
        $tokenLencount=count($this->arrValidTokenPos);
        $stack=array();
        $token=$this->arrToken[$this->arrValidTokenPos[$blockPosStart]];
        array_push($stack, $token);
        for($i=$blockPosStart+1;$i<$tokenLencount;$i++)
        {
            $token=$this->arrToken[$this->arrValidTokenPos[$i]];
            if(!is_array($token))
            {
                if($token==$blockEndDelimiter)
                {
                    array_pop($stack);
                    if(empty($stack))
                    {
                        return $i;
                    }
                }
                else if($token==$blockStartDelimiter)
                {
                    array_push($stack, $token);
                }
            }
        }
        return -1;
    }
    protected function getBlockEnd($blockPosStart,$blockStartDelimiter="{",$blockEndDelimiter="}")
    {
        $tokenLencount=count($this->arrToken);
        $stack=array();
        $token=$this->arrToken[$blockPosStart];
        array_push($stack, $token);
        for($i=$blockPosStart+1;$i<$tokenLencount;$i++)
        {
            $token=$this->arrToken[$i];
            if(!is_array($token))
            {
                if($token==$blockEndDelimiter)
                {
                    array_pop($stack);
                    if(empty($stack))
                    {
                        return $i;
                    }
                }
                else if($token==$blockStartDelimiter)
                {
                    array_push($stack, $token);
                }
            }
        }
        return -1;
    }
    protected function getFirstToken()
    {
        if(is_array($this->arrToken[0]) && $this->arrToken[0][0]===T_WHITESPACE)
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }
    
    protected function getColonBlockEnd($blockPosStart)
    {
        $tokenLencount=count($this->arrToken);
        $startToken=$this->arrToken[$blockPosStart];
        if(!is_array($startToken)) trace("Unexpected Colon Block start token (Number: {$blockPosStart}");
        $startTokenType=$startToken[0];
        if($startTokenType===T_IF) $endTokenType=T_ENDIF;
        else if($startTokenType===T_FOR) $endTokenType=T_ENDFOR;
        else if($startTokenType===T_FOREACH) $endTokenType=T_ENDFOREACH;
        else if($startTokenType===T_WHILE) $endTokenType=T_ENDWHILE;
        else trace("Unexpected Start Token Type at ({$blockPosStart})");
        $stack=array();
        array_push($stack,$startTokenType);
        for($i=$blockPosStart+1;$i<$tokenLencount;$i++)
        {
            $token=$this->arrToken[$i];
            if(is_array($token))
            {
                if($token[0]==$endTokenType)
                {
                    array_pop($stack);
                    if(empty($stack))
                    {
                        ///move pointer to statment delimiter(;)
                        $delimPos=$this->getNextToken($i);
                        $delimToken=$this->arrToken[$delimPos];
                        if(is_array($delimToken)) trace("Delimiter not found for Colon Block Statment");
                        return $delimPos;
                    }
                }
                else if($token[0]==$startTokenType)
                {
                    array_push($stack, $startTokenType);
                }
            }
        }
        return -1;
    }
    protected function getIfEndPos($tokenPos)
    {
        while(($tokenPos=$this->getNextToken($tokenPos))!==-1)
        {
            $token=$this->arrToken[$tokenPos];
            if(!is_array($token) && $token=="(")
            {
                $tokenPos=$this->getBlockEnd($tokenPos,"(",")");
                $tokenPos=$this->getNextToken($tokenPos);
                $token=$this->arrToken[$tokenPos];
                ///if the next token is array then this if has no block associated with it. single statement (simple or compound)
                if(is_array($token))
                {
                    $tmpPos=$this->isSimpleStatement($tokenPos);
                    //if($tmpPos===false)
                    $tokenPos = $this->getNextToken($tokenPos);
                }
                else
                {
                    if($token=="{")
                    {
                        $tokenPos=$this->getBlockEnd($tokenPos);
                    }
                }
                return $tokenPos;
            }
        }
    }
    
    ///get simple statment(excludes compound statement - statements with blocks). the parameter must be the start token's pos
    protected function isSimpleStatement($tokenPos)
    {
        if(empty($this->arrToken))
        {
            $this->arrError[]=array("error"=>"Empty Tokens","vars"=>  $vars,"this"=>$this);
            trace($this->arrError);
        }
        $startPos=$tokenPos;
        $token=$this->arrToken[$tokenPos];
        if(!is_array($token)) 
        {
            ///if the token starts with open brace, it indicates it is compound statement
            if($token=="{") return false;
            ///if the token starts with @, it can be simple statement. If not unexpected start token
            if($token!="@")  
            {
                $vars=get_defined_vars();
                $this->arrError[]=array("error"=>"Unexpected statement start token(number:{$tokenPos})","vars"=>  $vars,"this"=>$this);
                trace($this->arrError);
            }
        }
        ///statement without either ';' or '{ and }'
        if($token[0]===T_COMMENT || $token[0]===T_DOC_COMMENT || $token[0]===T_OPEN_TAG || $token[0]===T_CLOSE_TAG)
        {
            return $tokenPos;
        }
        if($token[0]===T_FUNCTION || $token[0]===T_IF || $token[0]===T_ELSE || $token[0]===T_ELSEIF || $token[0]===T_FOR || $token[0]===T_FOREACH || $token[0]===T_SWITCH || 
                $token[0]===T_TRY || $token[0]===T_CATCH || $token[0] === T_WHILE || $token[0] === T_DO  ) return false;
        
        ///other statements which end with semicolon
        while(($tokenPos=$this->getNextToken($tokenPos))!==-1)
        {
            $token=$this->arrToken[$tokenPos];
            if(is_array($token) && ($token[0]===T_COMMENT||$token[0]===T_DOC_COMMENT))
            {
                return $tokenPos;
            }
            if(!is_array($token) && $token==";")
            {
                return $tokenPos;
            }
            else if(is_array($token) && $token[0]===T_CLOSE_TAG)
            {
                /// since the statement does not has ';', moving the pointer to previous token. that is the last token of the statement
                return $tokenPos-1;
            }
        }
        return -1;
    }
}
?>